using System;
using System.Collections;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Threading;
using System.IO;
using System.Windows.Forms;

using Engine.Core;
using Engine.Connection;
using Engine.Connection.Client;
using Engine.Rendering;
using Engine.Math;

namespace Client
{
    public class GameForm
    {
        private static Mutex mutex = new Mutex();

        private RenderWindow window;
        private VideoSettings settings;
        private Microsoft.DirectX.Direct3D.Device device;

        private Camera camera;
        private Model player, brum;
        private Component camnode, camhnode, camdnode;
        private TCPClient connection;
        private ArrayList users;

        private int count;
        private int x, y;

        private Ray mRay;

        // Sexy delegate
        public delegate void ParseCallback(Packet package);

        // Kinky constructor ;-)
        public GameForm(VideoSettings settings)
        {
            // Initiate attributes
            this.count = 0;
            this.settings = settings;
            this.users = new ArrayList();
            this.window = new RenderWindow("DoubleJ - Alpha v0.1");

            // Initiate connection
            connection = new TCPClient("game");

            // Attach event
            connection.DataRecieved += new PackageEventHandler(connection_DataRecieved);

            // Load Meshes
            MeshManager.Instance.LoadMesh("tiger", "data/model/tiger.x", MeshType.MT_MODEL);
            MeshManager.Instance.LoadMesh("ninja", "data/model/dildo.x", MeshType.MT_MODEL);
            MeshManager.Instance.LoadMesh("car", "data/model/car.x", MeshType.MT_MODEL);
            MeshManager.Instance.LoadMesh("ship", "data/model/bigship.x", MeshType.MT_MODEL);

            if (window.InitDevice(settings.GetWidth(), settings.GetHeight(), settings.GetFullscreen())) 
            { device = window.Device; }

            // Setup Terrain
            window.Terrain = new Terrain("test", "data/heightmap.bmp", TerrainType.TP_BMP);
            window.Terrain.Maxheight = 10;
            window.Terrain.Init(device);

            // Setup Skybox
            window.Skybox = new Skybox(""); // Standard skybox, argument is prefix for texture files, if given texture will be "prefixback.jpg" "prefixfront.jpg" etc etc.
            window.Skybox.Init(device);

            // Setup Lights
            device.Lights[0].Type = Microsoft.DirectX.Direct3D.LightType.Directional;
            device.Lights[0].Diffuse = Color.White;
            device.Lights[0].Ambient = Color.White;
            device.Lights[0].Direction = new Microsoft.DirectX.Vector3(-0.5f, -1f, 0);
            device.Lights[0].Enabled = true;

            player = new Model(settings.GetName(), "tiger");
            brum = new Model("Brum", "car");

            camnode = new Component("CameraNode");
            camhnode = new Component("CameraHeightNode");
            camdnode = new Component("CameraDistanceNode");

            camera = new Camera("Default Camera");
            camera.SetParent(camnode);
            camnode.SetParent(player);
            camera.Translate(0, 0, -10);

            player.Translate(20, 40, 20);

            // Yourie's ray crap
            window.AddRenderable(player);
            window.AddRenderable(brum);
            mRay = new Ray(player.WorldPosition, Vector3.UNIT_NEGATIVE_Y);
            float p = window.Terrain.Intersects(mRay);
            Vector3 pos = mRay.Point(p);
            Vector3 transpos = pos - player.WorldPosition;
            player.Translate(transpos);
            player.Translate(0, 1, 0);

            // Setup camera
            window.Camera = camera;

            // Connect to server
            if (connection.Connect(settings.GetIp(), settings.GetPort()))
            {
                String s = "/update " + player.Name + " " + player.WorldPosition.x.ToString() + " " + player.WorldPosition.y.ToString() + " " + player.WorldPosition.z.ToString() + " ";
                
                // Send initial update after connecting
                connection.Send(Encoding.ASCII.GetBytes(s));
            }

            // Add connection diagnostics
            window.AddDiagnostic(player);
            window.AddDiagnostic(connection);

            // Setup culture info (required for float parsing)
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");

            // Attach events
            window.MouseMove += new MouseEventHandler(window_MouseMove);
            window.MouseClick += new MouseEventHandler(window_MouseClick);
            window.KeyPress += new KeyPressEventHandler(window_KeyPress);
            window.FormClosed += new FormClosedEventHandler(window_FormClosed);
        }

        public void Close()
        {
            // Close settings
            settings.Close();

            // Close main window
            MainWindow.Instance.Close();

            // Dispose render window
            window.Dispose();
        }

        // Client callbacks
        private void connection_DataRecieved(object sender, Packet package)
        {
            if (package.DataBufferStream == null)
            {
                String data = new String(Encoding.ASCII.GetChars(package.DataBuffer));

                // Update player
                if (data.StartsWith("/update"))
                {
                    string[] info = data.Split(' ');
                    string name = info[1];
                    float x = float.Parse(info[2]);
                    float y = float.Parse(info[3]);
                    float z = float.Parse(info[4]);

                    mutex.WaitOne();

                    // Search node
                    foreach (Component n in users)
                    {
                        if (n.Name.Equals(name))
                        {
                            // Apply change
                            n.Position = new Vector3(x, y, z);

                            // Stop searching
                            mutex.ReleaseMutex();
                            return;
                        }
                    }

                    mutex.ReleaseMutex();

                    // New player
                    if (! name.Equals(player.Name))
                    {
                        Model m = new Model(name, "tiger");
                        m.Position = new Vector3(x, y, z);

                        mutex.WaitOne();

                        // Add to collection
                        users.Add(m);

                        // Add to renderwindow
                        window.AddRenderable(m);

                        mutex.ReleaseMutex();
                    }
                }

                // Update name
                else if (data.StartsWith("/name"))
                {
                    string[] info = data.Split(' ');
                    player.Name = info[1];
                }
            }
        }

        // Form callbacks
        void window_KeyPress(object sender, KeyPressEventArgs e)
        {
            switch (e.KeyChar)
            {
                // Translations
                case 'w': player.Translate(0, 0, 1.0f);
                    break;
                case 's': player.Translate(0, 0, -1.0f);
                    break;
                case 'a': player.Translate(-1.0f, 0, 0);
                    break;
                case 'd': player.Translate(1.0f, 0, 0);
                    break;
                case 'e': player.Translate(0, -1.0f, 0);
                    break;
                case 'q': player.Translate(0, 1.0f, 0);
                    break;

                // Terrain
                case '+': window.Terrain.Maxheight += 2;
                    break;
                case '-': window.Terrain.Maxheight -= 2;
                    break;

                // Spawn
                case 'm':
                    count++;

                    Model ship = new Model("schipie #" + count, "ship");
                    ship.Position = new Vector3(count * 10, count * 10, count * 10);

                    window.AddRenderable(ship);

                    break;
                case 'n':
                    count++;

                    Model car = new Model("autotje #" + count, "car");
                    car.Position = new Vector3(count * 10, count * 10, count * 10);

                    window.AddRenderable(car);

                    break;

                // Rendering
                case 'l': device.RenderState.FillMode = Microsoft.DirectX.Direct3D.FillMode.Solid;
                    break;
                case 'k': device.RenderState.FillMode = Microsoft.DirectX.Direct3D.FillMode.WireFrame;
                    break;
                case 'j': device.RenderState.FillMode = Microsoft.DirectX.Direct3D.FillMode.Point;
                    break;

                // Close
                case 'x': this.Close();
                    break;                                                                  
            }

            mRay.Origin = player.WorldPosition;
            float p = window.Terrain.Intersects(mRay);
            
            if(p > 0)
            {
                Vector3 pos = mRay.Point(p);
                Vector3 transpos = pos - player.WorldPosition;
                player.Translate(transpos);
                player.Translate(0, 2, 0);
            }

            // Update position
            String s = "/update " + player.Name + " " + player.WorldPosition.x.ToString() + " " + player.WorldPosition.y.ToString() + " " + player.WorldPosition.z.ToString() + " ";
            if (connection.Connected) connection.Send(Encoding.ASCII.GetBytes(s));
        }

        void window_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {

            }
        }

        void window_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button.Equals(MouseButtons.Right))
            {
                camnode.Pitch((e.Y - y) / 2.5f);
                player.Yaw((e.X - x) / 2.5f);
            }

            else if (e.Button.Equals(MouseButtons.Middle))
            {
                // Distance
            }
            y = e.Y;
            x = e.X;
        }

        void window_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.Close();
        }

        // Acessors
        public RenderWindow Window
        {
            get { return window; }
        }

        public Microsoft.DirectX.Direct3D.Device Device
        {
            get { return device; }
        }

        public void handleInput()
        {
            // handle input with directinput
        }

    }
}
